package com.xiebishe.mylibrary.common.image;


import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.widget.AppCompatImageView;

import com.xiebishe.mylibrary.R;


/**
 * @class RoundImageView
 * @describe 圆角ImageView
 */
public class RoundImageView extends AppCompatImageView {

    private static final String TAG = "RoundImageView";

    private static final int DEFAULT_RADIUS = 0;
    private Paint mPaint;
    private int mRadius = DEFAULT_RADIUS;
    private BitmapShader shader;
    private Bitmap temp;
    private Path path = new Path();
    private RectF tempRectF = new RectF();
    private float[] floats = new float[9];
    private int mRadiusTopLeft;
    private int mRadiusTopRight;
    private int mRadiusBottomLeft;
    private int mRadiusBottomRight;
    private boolean outlineMode = false;
    private Bitmap bufferBitmap;
    private Canvas bufferCanvas;
    private boolean isBitmapDrawn;
    private Matrix mMatrix = new Matrix();
    private ViewOutlineProvider viewOutlineProvider = new ViewOutlineProvider() {
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void getOutline(View view, Outline outline) {
            outline.setRoundRect(view.getPaddingLeft(), view.getPaddingTop(), view.getWidth() - view.getPaddingRight(), view.getHeight() - view.getPaddingBottom(), mRadiusBottomLeft);
        }
    };

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public RoundImageView(Context context) {
        super(context);
        init(null);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public RoundImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public RoundImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }


    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //如果大小变化了 因为除了位图，其他方式的drawable可能会根据大小发生变化，那么删掉已经创建的shader）

        if (outlineMode) {
            return;
        }
        if (!(getDrawable() instanceof BitmapDrawable)) {
            if (changed) createShader(null);
        }
        //大小不变，matrix不变,综合setImageDrawable 判定是否已绘
        if (!changed && mMatrix.equals(getImageMatrix())) isBitmapDrawn = true;
        if (changed || !mMatrix.equals(getImageMatrix())) {
            if (shader == null) {
                createShader(getDrawable());
            }
            if (shader != null) {
                updateMatrixAndDrawPath(getImageMatrix());
            }
        }
    }

    @Override
    public void setImageDrawable(@Nullable Drawable drawable) {
        super.setImageDrawable(drawable);
        if (shader != null) {
            createShader(null);
        }
//        createShader(drawable);
    }

    @Override
    public void invalidateDrawable(@NonNull Drawable dr) {
        super.invalidateDrawable(dr);
        createShader(null);
    }

    private void createShader(Drawable drawable) {
        isBitmapDrawn = false;

        if (temp != null && !temp.isRecycled()) {
            temp.recycle();
            temp = null;
        }

        if (drawable == null) {
            shader = null;
            if (bufferBitmap != null && !bufferBitmap.isRecycled()) {
                bufferBitmap.recycle();
                bufferBitmap = null;
                bufferCanvas = null;
            }
            return;
        }

        if (drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) return;
        if (shader == null) {

            if (bufferBitmap != null && !bufferBitmap.isRecycled()) {
                bufferBitmap.recycle();
                bufferBitmap = null;
                bufferCanvas = null;
            }
            if (drawable instanceof BitmapDrawable) {
                if (((BitmapDrawable) drawable).getBitmap() != null) {
                    shader = new BitmapShader(((BitmapDrawable) drawable).getBitmap(), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
                    bufferBitmap = Bitmap.createBitmap(((BitmapDrawable) drawable).getBitmap().getWidth(), ((BitmapDrawable) drawable).getBitmap().getHeight(), Bitmap.Config.ARGB_8888);
                }
            } else {
                int w = getWidth() - getPaddingLeft() - getPaddingRight();
                int h = getHeight() - getPaddingTop() - getPaddingBottom();
                if (w > 0 && h > 0) {
                    if (drawable.getIntrinsicWidth() > 0 && drawable.getIntrinsicHeight() > 0) {
                        bufferBitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
                    } else {
                        bufferBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
                    }
                    temp = bufferBitmap.copy(Bitmap.Config.ARGB_8888, true);
                    Canvas canvas = new Canvas(temp);
                    drawable.draw(canvas);
                    shader = new BitmapShader(temp, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
                }
            }

            if (bufferBitmap != null && !bufferBitmap.isRecycled()) {
                bufferCanvas = new Canvas(bufferBitmap);
            }
        }

    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void init(AttributeSet attrs) {

        if (attrs != null) {
            TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.RoundImageView);
            mRadius = a.getDimensionPixelSize(R.styleable.RoundImageView_RIV_radius, DEFAULT_RADIUS);
            mRadiusTopLeft = a.getDimensionPixelSize(R.styleable.RoundImageView_RIV_radius_top_left, mRadius);
            mRadiusTopRight = a.getDimensionPixelSize(R.styleable.RoundImageView_RIV_radius_top_right, mRadius);
            mRadiusBottomLeft = a.getDimensionPixelSize(R.styleable.RoundImageView_RIV_radius_bottom_left, mRadius);
            mRadiusBottomRight = a.getDimensionPixelSize(R.styleable.RoundImageView_RIV_radius_bottom_right, mRadius);
            a.recycle();
            setRadius(mRadiusTopLeft, mRadiusTopRight, mRadiusBottomLeft, mRadiusBottomRight);
        }
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }

    public int getRadius() {
        return mRadius;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void setRadius(int radius) {
        this.mRadius = radius;
        setRadius(radius, radius, radius, radius);
    }


    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void setRadius(int leftTop, int rightTop, int leftBottom, int rightBottom) {
        mRadiusTopLeft = leftTop;
        mRadiusTopRight = rightTop;
        mRadiusBottomLeft = leftBottom;
        mRadiusBottomRight = rightBottom;
        isBitmapDrawn = false;
        if (isRoundRect()) {
            if (getBackground() == null) {
                outlineMode = true;
            }
        }
        if (outlineMode) {
            createShader(null);
            setClipToOutline(true);
            setOutlineProvider(viewOutlineProvider);
        } else {
            setClipToOutline(false);
            setOutlineProvider(null);
            if (getImageMatrix() != null && shader != null) {
                updateMatrixAndDrawPath(getImageMatrix());
            }
        }
        postInvalidate();
    }

    public boolean isRoundRect() {
        return mRadiusBottomLeft == mRadiusBottomRight && mRadiusBottomRight == mRadiusTopLeft && mRadiusTopLeft == mRadiusTopRight;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void setBackground(Drawable background) {
        super.setBackground(background);
        if (background != null) {
            outlineMode = false;
            setClipToOutline(false);
            setOutlineProvider(null);
        } else if (isRoundRect()) {
            outlineMode = true;
            setClipToOutline(true);
            setOutlineProvider(viewOutlineProvider);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onDraw(Canvas canvas) {
        Drawable mDrawable = getDrawable();
        if (mDrawable == null) {
            return; // couldn't resolve the URI
        }
        if (mDrawable.getIntrinsicWidth() == 0 || mDrawable.getIntrinsicHeight() == 0) {
            return;     // nothing to draw (empty bounds)
        }

        //圆角
        if (outlineMode) {
            super.onDraw(canvas);
        } else {
            final int scrollX = getScrollX();
            final int scrollY = getScrollY();
            if (getCropToPadding()) {
                canvas.clipRect(scrollX + getPaddingLeft(), scrollY + getPaddingTop(),
                        scrollX + getRight() - getLeft() - getPaddingRight(),
                        scrollY + getBottom() - getTop() - getPaddingBottom());
            } else {
                canvas.translate(getPaddingLeft(), getPaddingTop());
            }
            final int saveCount = canvas.save();

            if (isBitmapDrawn && bufferBitmap != null && !bufferBitmap.isRecycled() && shader != null) {
                canvas.drawBitmap(bufferBitmap, 0, 0, null); //直接绘制bitmap
            } else {
                if (shader == null) createShader(mDrawable);
                if (shader != null && bufferBitmap != null && !bufferBitmap.isRecycled() && mPaint != null) {
                    updateMatrixAndDrawPath(getImageMatrix());
                    canvas.drawBitmap(bufferBitmap, 0, 0, null);
                }
            }
            canvas.restoreToCount(saveCount);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void updateMatrixAndDrawPath(Matrix matrix) {
        if (mRadiusTopLeft > 0 || mRadiusTopRight > 0 || mRadiusBottomLeft > 0 || mRadiusBottomRight > 0) {
            mMatrix.set(matrix);
            final int save = bufferCanvas.save();
            if (getDrawable() instanceof BitmapDrawable) {
                Bitmap bitmap = ((BitmapDrawable) getDrawable()).getBitmap();
                int scaledWidth = bitmap.getScaledWidth(getContext().getResources().getDisplayMetrics());
                int scaledHeight = bitmap.getScaledHeight(getContext().getResources().getDisplayMetrics());
                if (bitmap.getWidth() != scaledWidth) {
                    //如果bitmap在不符合对应dpi的文件夹下取出，那么展示的宽和高和bitmap的宽和高不对等，需要提前进行缩放
                    matrix.preScale((float) scaledWidth / bitmap.getWidth(), (float) scaledHeight / bitmap.getHeight());
                }
            }

            matrix.getValues(floats);
            float xScale = floats[Matrix.MSCALE_X];
            float yScale = floats[Matrix.MSCALE_Y];
            bufferCanvas.concat(matrix);
            mPaint.setShader(shader);

            bufferCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); //清除bufferBitmap信息
            if (isRoundRect()) {
                //画圆角矩阵
                tempRectF.set(0, 0, bufferBitmap.getWidth(), bufferBitmap.getHeight());
                float xRadius = mRadiusBottomLeft / xScale;
                float yRadius = mRadiusBottomLeft / yScale;
                bufferCanvas.drawRoundRect(tempRectF, xRadius, yRadius, mPaint);
            } else {
                //圆角不一致，画path 每个角都是椭圆（）
                float xRadius, yRadius;
                path.rewind();
                //左上角
                xRadius = mRadiusTopLeft / xScale;
                yRadius = mRadiusTopLeft / yScale;
                path.moveTo(0f, yRadius);
                path.arcTo(0f, 0f, xRadius + xRadius, yRadius + yRadius, 180f, 90f, false);
                //右上角
                xRadius = mRadiusTopRight / xScale;
                yRadius = mRadiusTopRight / yScale;
                path.lineTo(bufferBitmap.getWidth() - xRadius, 0f);
                path.arcTo(bufferBitmap.getWidth() - xRadius - xRadius, 0, bufferBitmap.getWidth(), yRadius + yRadius, 270f, 90f, false);
                //右下角
                xRadius = mRadiusBottomRight / xScale;
                yRadius = mRadiusBottomRight / yScale;
                path.lineTo(bufferBitmap.getWidth(), bufferBitmap.getHeight() - yRadius);
                path.arcTo(bufferBitmap.getWidth() - xRadius - xRadius, bufferBitmap.getHeight() - yRadius - yRadius, bufferBitmap.getWidth(), bufferBitmap.getHeight(), 0, 90f, false);
                //左下角
                xRadius = mRadiusBottomLeft / xScale;
                yRadius = mRadiusBottomLeft / yScale;
                path.lineTo(xRadius, bufferBitmap.getHeight());
                path.arcTo(0, bufferBitmap.getHeight() - yRadius - yRadius, xRadius + xRadius, bufferBitmap.getHeight(), 90, 90f, false);
                //封闭
                path.close();
                bufferCanvas.drawPath(path, mPaint);
            }
            isBitmapDrawn = true;
            bufferCanvas.restoreToCount(save);
        }
    }
}